using System;
using System.IO;
using FireStarter.CodeGeneration;
using FireStarterModeller.BO;
using NUnit.Framework;

namespace FireStarter.Test.CodeGeneration
{
    [TestFixture]
    public class TestProjectFileManager : TestBase
    {
        private string TEST_PROJECTS_FOLDER = "TestProjects";

        [SetUp]
        public void SetupTest()
        {
            if (Directory.Exists(TEST_PROJECTS_FOLDER))
            {
                try
                {
                    Directory.Delete(TEST_PROJECTS_FOLDER, true);
                }
                catch (Exception)
                {
                    
                }
                
            }
        }

        #region Utility Methods

        private string GetTestProjectFileName()
        {
            string projectName = GetRandomString();
            return TEST_PROJECTS_FOLDER + Path.DirectorySeparatorChar + projectName + Path.DirectorySeparatorChar +
                   projectName + ".csproj";
        }

        private string GetExpectedContents(string projectFileName, Guid projectGuid)
        {
            string projectName = Path.GetFileNameWithoutExtension(projectFileName);
            string expectedContents = TemplateLoader.Csproj(CodeTemplateEnum.CSharpVisualStudio2005);
            expectedContents = expectedContents.Replace("<ProjectGuid></ProjectGuid>",
                                                        string.Format("<ProjectGuid>{0}</ProjectGuid>", projectGuid.ToString("B")));
            expectedContents = expectedContents.Replace("<AssemblyName></AssemblyName>",
                                                        string.Format("<AssemblyName>{0}</AssemblyName>", projectName));
            expectedContents = expectedContents.Replace("<RootNamespace></RootNamespace>",
                                                        string.Format("<RootNamespace>{0}</RootNamespace>", projectName));
            expectedContents = expectedContents.Replace("<OutputType></OutputType>",
                                                        string.Format("<OutputType>{0}</OutputType>", OutputType.Library));
            return expectedContents;
        }

        private void CreateTestProjectFile(string projectFileName)
        {
            CreateTestProjectFile(projectFileName, Guid.NewGuid());
        }

        private void CreateTestProjectFile(string projectFileName, Guid projectGuid)
        {
            string projectFileContents = GetExpectedContents(projectFileName, projectGuid);
            Directory.CreateDirectory(Path.GetDirectoryName(projectFileName));
            File.AppendAllText(projectFileName, projectFileContents);
        }

        private ProjectFileManager CreateProjectFileManager()
        {
            string projectFileName = GetTestProjectFileName();
            CreateTestProjectFile(projectFileName);
            return new ProjectFileManager(projectFileName, OutputType.Library, CodeTemplateEnum.CSharpVisualStudio2005);
        }

        #endregion // Utility Methods

        [Test]
        public void TestCreateEmptyProjectFile()
        {
            //---------------Set up test pack-------------------
            string projectFileName = GetTestProjectFileName();
            string projectName = Path.GetFileNameWithoutExtension(projectFileName);
            ProjectFileManager projectFileManager = new ProjectFileManager(projectFileName, OutputType.Library, CodeTemplateEnum.CSharpVisualStudio2005);
            string expectedContents = GetExpectedContents(projectFileName, projectFileManager.ProjectGuid);
            //---------------Assert Precondition----------------
            Assert.IsFalse(File.Exists(projectFileName));
            //---------------Execute Test ----------------------
            projectFileManager.SaveProjectFile();
            //---------------Test Result -----------------------
            Assert.IsTrue(File.Exists(projectFileName));
            string contents = File.ReadAllText(projectFileName);
            Assert.AreEqual(expectedContents, contents);
            Assert.IsFalse(projectFileManager.IsWebProject);
            Assert.IsTrue(string.IsNullOrEmpty(projectFileManager.ProjectTypeGuids));
        }

        [Test]
        public void TestOpenExistingProjectFile()
        {
            //---------------Set up test pack-------------------
            string projectFileName = GetTestProjectFileName();
            Guid projectGuid = Guid.NewGuid();
            CreateTestProjectFile(projectFileName, projectGuid);
            string projectName = Path.GetFileNameWithoutExtension(projectFileName);
            //---------------Assert Precondition----------------
            Assert.IsTrue(File.Exists(projectFileName));
            //---------------Execute Test ----------------------
            ProjectFileManager projectFileManager = new ProjectFileManager(projectFileName, OutputType.Library, CodeTemplateEnum.CSharpVisualStudio2005);
            //---------------Test Result -----------------------
            Assert.IsTrue(File.Exists(projectFileName));
            Assert.AreEqual(projectGuid, projectFileManager.ProjectGuid);
            Assert.AreEqual(projectName, projectFileManager.AssemblyName);
            Assert.AreEqual(projectName, projectFileManager.RootNamespace);
            Assert.AreEqual(OutputType.Library, projectFileManager.OutputType);
        }

        [Test]
        public void TestFileNameOnlyConstructor_OpenExistingProjectFile()
        {
            //---------------Set up test pack-------------------
            string projectFileName = GetTestProjectFileName();
            Guid projectGuid = Guid.NewGuid();
            CreateTestProjectFile(projectFileName, projectGuid);
            string projectName = Path.GetFileNameWithoutExtension(projectFileName);
            //---------------Assert Precondition----------------
            Assert.IsTrue(File.Exists(projectFileName));
            //---------------Execute Test ----------------------
            ProjectFileManager projectFileManager = new ProjectFileManager(projectFileName, CodeTemplateEnum.CSharpVisualStudio2005);
            //---------------Test Result -----------------------
            Assert.IsTrue(File.Exists(projectFileName));
            Assert.AreEqual(projectGuid, projectFileManager.ProjectGuid);
            Assert.AreEqual(projectName, projectFileManager.AssemblyName);
            Assert.AreEqual(projectName, projectFileManager.RootNamespace);
            Assert.AreEqual(OutputType.Library, projectFileManager.OutputType);
        }

        [Test]
        public void TestAddCodeFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string fileName = CreateRandomFile("cs");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasCodeFile(fileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddCodeFile(fileName);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasCodeFile(fileName));
        }

        [Test]
        public void TestAddCodeFile_WithDependancyOnFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string projectName = Path.GetFileNameWithoutExtension(projectFileManager.ProjectFileName);
            string fileName = CreateRandomFile(projectName, "cs");
            string dependancyFile = CreateRandomFile(projectName, "cs");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasCodeFile(fileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddCodeFile(fileName, dependancyFile);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasCodeFile(fileName));
            Assert.IsTrue(projectFileManager.HasCodeFile(fileName, dependancyFile));
        }

        [Test]
        public void TestHasCodeFile_WithDependancyOnFile_Fails()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string fileName = CreateRandomFile("cs");
            string dependancyFile = CreateRandomFile("cs");
            projectFileManager.AddCodeFile(fileName, dependancyFile);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            bool result = projectFileManager.HasCodeFile(fileName, "aaa");
            //---------------Test Result -----------------------
            Assert.IsFalse(result);
        }
        [Test]
        public void TestAddContentFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string fileName = CreateRandomFile("cs");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasContentFile(fileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddContentFile(fileName);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasContentFile(fileName));
        }

        [Test]
        public void TestAddContentFile_WithDependancyOnFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string fileName = CreateRandomFile("cs");
            string dependancyFile = CreateRandomFile("cs");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasContentFile(fileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddContentFile(fileName, dependancyFile);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasContentFile(fileName));
            Assert.IsTrue(projectFileManager.HasContentFile(fileName, dependancyFile));
        }

        [Test]
        public void TestAddFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string fileName = CreateRandomFile(GetRandomString().Substring(0,3));
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasFile(fileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddFile(fileName);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasFile(fileName));
        }

        [Test]
        public void TestAddResourceFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string folderName = GetRandomString();
            string resourceFileName = CreateRandomFile(folderName, "resx");
            string designerFileName = CreateRandomFile(folderName, "cs");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasResourceFile(resourceFileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddResourceFile(resourceFileName, designerFileName);
            projectFileManager.SaveProjectFile();
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasResourceFile(resourceFileName));
        }

        [Test]
        public void TestAddEmbeddedResourceFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string folderName = GetRandomString();
            string resourceFileName = CreateRandomFile(folderName, "xml");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasResourceFile(resourceFileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddEmbeddedResourceFile(resourceFileName);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasResourceFile(resourceFileName));
        }

        [Test]
        public void TestAddEmbeddedResourceFile_WhenAlreadyExistsInAnotherGroup_ShouldNotAddAgain()
        {
            //---------------Set up test pack-------------------
            string projectFileName = GetTestProjectFileName();
            string folderName = GetRandomString();
            string fileNameOnly = Path.GetFileName(CreateRandomFile(folderName, "xml"));
            string resourceFileName = Path.Combine(Path.GetDirectoryName(projectFileName), fileNameOnly);

            string csProjXml = string.Format(@"
                <Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""3.5"">
                  <ItemGroup>
                    <Compile Include=""Properties\AssemblyInfo.cs"" />
                  </ItemGroup>
                  <ItemGroup>
                    <EmbeddedResource Include=""{0}"" />
                  </ItemGroup>
                </Project>
                ", fileNameOnly);
            ProjectFileManager projectFileManager = new ProjectFileManager(projectFileName, OutputType.Library, false, csProjXml, CodeTemplateEnum.CSharpVisualStudio2005);

            //---------------Assert Precondition----------------
            Assert.IsTrue(projectFileManager.HasResourceFile(resourceFileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddEmbeddedResourceFile(resourceFileName);
            //---------------Test Result -----------------------
            string projectFileXml = projectFileManager.GetCurrentProjectFileXml();
            Assert.IsTrue(projectFileManager.HasResourceFile(resourceFileName));
            //Split up the contents of the project file into the parts divided by the resource file name
            // if there are only 2 parts then this means that there was only one occurence of the resource file name
            string[] parts = projectFileXml.Split(new[] { fileNameOnly }, StringSplitOptions.None);
            Assert.AreEqual(1, parts.Length - 1, 
                string.Format("Should only have one occurrence of '{0}' in :" + 
                Environment.NewLine + "{1}", fileNameOnly, projectFileXml));
        }

        [Test]
        public void TestAddSettingsFile()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string folderName = GetRandomString();
            string settingsFileName = CreateRandomFile(folderName, "settings");
            string designerFileName = CreateRandomFile(folderName, "cs");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasSettingsFile(settingsFileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddSettingsFile(settingsFileName, designerFileName);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasSettingsFile(settingsFileName));
        }

        [Test]
        public void TestAddReference()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string dllFileName = CreateRandomFile("dll");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasReference(dllFileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddReference(dllFileName);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasReference(dllFileName));
        }

        [Test]
        public void TestAddReference_WhenAlreadyExistsInAnotherGroup_ShouldNotAddAgain()
        {
            //---------------Set up test pack-------------------
            string projectFileName = GetTestProjectFileName();
            string folderName = GetRandomString();
            string dllFileName = Path.Combine(Path.GetDirectoryName(projectFileName), 
                Path.GetFileName(CreateRandomFile(folderName, "dll")));
            string dllNameOnly = Path.GetFileNameWithoutExtension(dllFileName);

            string csProjXml = string.Format(@"
                <Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""3.5"">
                  <ItemGroup>
                    <Reference Include=""System"" />
                  </ItemGroup>
                  <ItemGroup>
                    <Reference Include=""{0}"" />
                  </ItemGroup>
                </Project>
                ", dllNameOnly);
            ProjectFileManager projectFileManager = new ProjectFileManager(projectFileName, OutputType.Library, false, csProjXml, CodeTemplateEnum.CSharpVisualStudio2005);

            //---------------Assert Precondition----------------
            //Assert.IsTrue(projectFileManager.HasReference(dllFileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddReference(dllFileName);
            //---------------Test Result -----------------------
            string projectFileXml = projectFileManager.GetCurrentProjectFileXml();
            Assert.IsTrue(projectFileManager.HasReference(dllFileName));
            //Split up the contents of the project file into the parts divided by the resource file name
            // if there are only 2 parts then this means that there was only one occurence of the resource file name
            string[] parts = projectFileXml.Split(new[] { "\"" + dllNameOnly + "\"" }, StringSplitOptions.None);
            Assert.AreEqual(1, parts.Length - 1,
                string.Format("Should only have one occurrence of reference '{0}' in :" +
                Environment.NewLine + "{1}", dllNameOnly, projectFileXml));
        }

        [Test]
        public void TestAddSystemReference()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager projectFileManager = CreateProjectFileManager();
            string dllFileName = CreateRandomFile("dll");
            //---------------Assert Precondition----------------
            Assert.IsFalse(projectFileManager.HasSystemReference(dllFileName));
            //---------------Execute Test ----------------------
            projectFileManager.AddSystemReference(dllFileName);
            //---------------Test Result -----------------------
            Assert.IsTrue(projectFileManager.HasSystemReference(dllFileName));
        }

        [Test]
        public void TestAddProjectReference()
        {
            //---------------Set up test pack-------------------
            ProjectFileManager mainProjectFileManager = CreateProjectFileManager();
            
            ProjectFileManager relatedProjectFileManager = CreateProjectFileManager();
            relatedProjectFileManager.SaveProjectFile();
            string relatedProjectFileName = relatedProjectFileManager.ProjectFileName;
            //---------------Assert Precondition----------------
            Assert.IsFalse(mainProjectFileManager.HasProjectReference(relatedProjectFileName));
            //---------------Execute Test ----------------------
            string relatedProjectName = Path.GetFileNameWithoutExtension(relatedProjectFileName);
            string solutionDirectory = Environment.CurrentDirectory + Path.DirectorySeparatorChar + TEST_PROJECTS_FOLDER;
            mainProjectFileManager.AddProjectReference(relatedProjectName, solutionDirectory);
            //---------------Test Result -----------------------
            Assert.IsTrue(mainProjectFileManager.HasProjectReference(relatedProjectFileName));
        }

        [Test]
        public void Test_WebConstructor()
        {
            //---------------Set up test pack-------------------
            string projectFileName = GetTestProjectFileName();    
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------

            ProjectFileManager projectFileManager = new ProjectFileManager(projectFileName, OutputType.Library, true, CodeTemplateEnum.CSharpVisualStudio2005);
            projectFileManager.SaveProjectFile();
 
            //---------------Test Result -----------------------

            string expectedContents = TemplateLoader.CsprojVWG(CodeTemplateEnum.CSharpVisualStudio2005);
            Assert.IsTrue(projectFileManager.IsWebProject);
            Assert.IsFalse(string.IsNullOrEmpty(projectFileManager.ProjectTypeGuids));
            StringAssert.Contains(ProjectFileManager.WEB_PROJECT_GUIDS, expectedContents);
        }

        [Test]
        public void Test_GetFullPath_ProjectNameNull_ReturnsNull()
        {
            //---------------Set up test pack-------------------
            string projectName = null;
            string solutionDirectory = TestUtilsCodeGen.GetRandomString();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string fullPath = ProjectFileManager.GetFullProjectPath(projectName, solutionDirectory);
            //---------------Test Result -----------------------
            Assert.IsNull(fullPath);
        }

        [Test]
        public void Test_GetFullPath_SolutionDirectoryNull_ReturnsProjectName()
        {
            //---------------Set up test pack-------------------
            string projectName = TestUtilsCodeGen.GetRandomString();
            string solutionDirectory = null;
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string fullPath = ProjectFileManager.GetFullProjectPath(projectName, solutionDirectory);
            //---------------Test Result -----------------------
            Assert.AreEqual(projectName, fullPath);
        }

        [Test]
        public void Test_GetFullPath_ReturnsFullPath()
        {
            //---------------Set up test pack-------------------
            string projectName = TestUtilsCodeGen.GetRandomString();
            string solutionDirectory = TestUtilsCodeGen.GetRandomString() + ".csproj";
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string fullPath = ProjectFileManager.GetFullProjectPath(projectName, solutionDirectory);
            //---------------Test Result -----------------------
            string directoryName = Path.GetFileNameWithoutExtension(projectName);
            string expectedFullPath = solutionDirectory + Path.DirectorySeparatorChar + directoryName +
                Path.DirectorySeparatorChar + projectName + ".csproj";
            Assert.AreEqual(expectedFullPath, fullPath);
        }

        private string CreateRandomFile(string extension)
        {
            return CreateRandomFile("", extension);
        }

        private string CreateRandomFile(string folderName, string extension)
        {
            string fileName = Path.Combine(Path.Combine(TEST_PROJECTS_FOLDER, folderName), GetRandomString() + "." + extension);
            string path = Path.GetDirectoryName(fileName);
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);
            File.AppendAllText(fileName, GetRandomString());
            return fileName;
        }


    }
}